import socket,sys
from xml.dom import minidom

class UxmlPdu:
    def __init__(self):#, site, tariff):
#        self.ussd_site = site
#        self.ttick_tariff = tariff

#Set the name values for the pairs    

        self.pairs={"PDU":"","MSISDN":"","STRING":"",\
                    "TID":"","REQID":"","ENCODING":"",\
                    "TARIFF":"","STATUS":""}
        
    def getParamCount(self):
#getParamCount Get the number of data (Name & Value) pairs
        return len(self.pairs)
    
    def clearValues(self):
#clearValues Initialias all the value entries of the pairs
        for i,j in self.pairs.iteritems():
            self.pairs[i] = ""
    
    def getPairs(self):
#getPairs This is for internal use. Bulk get of the name value pair.
        return self.pairs
    
    def setPairs(self,pairs):
#setPairs This is for internal use. Bulk set of the name value pair.
        self.pairs = pairs
    
    def setPdu(self,pdu):
        self.pairs["PDU"] = pdu
#setPdu Consult UXML spec for legal values

    def getPdu(self):
#getPdu Consult UXML spec for legal values
        return self.pairs["PDU"]
    
    def setMsisdn(self,msisdn):
#setMsisdn Consult UXML spec for legal values
        return self.pairs["MSISDN"]
    
    def getMsisdn(self):
#getMsisdn Consult UXML spec for legal values
        return self.pairs["MSISDN"]

    def setString(self,theString):
#setString Consult UXML spec for legal values
        self.pairs["STRING"] = theString
    
    def getString(self):
#getString Consult UXML spec for legal values
        return self.pairs["STRING"]
    
    def setTid(self,tid):
        self.pairs["TID"] = tid
#setTid Consult UXML spec for legal values

    def getTid(self):
#getTid Consult UXML spec for legal values
        return self.pairs["TID"]
    
    def setReqid(self,reqid):
#setReqid Consult UXML spec for legal values
        self.pairs["REQID"] = reqid

    def getReqid(self):
#getReqid Consult UXML spec for legal values
        return self.pairs["REQID"]

    def setEncoding(self,encoding):
#setEncoding Consult UXML spec for legal values
        self.pairs["ENCODING"] = encoding

    def getEncoding(self):
#getEncoding Consult UXML spec for legal values
        return self.pairs["ENCODING"] 

    def setTariff(self, tariff):
#setTariff Consult UXML spec for legal values
        self.pairs["TARIFF"] = tariff

    def getTariff(self):
#getTariff Consult UXML spec for legal values
        return self.pairs["TARIFF"]

    def setStatus(self, status):
#setStatus Consult UXML spec for legal values
        self.pairs["STATUS"] =  status

    def getStatus(self):
#getStatus Consult UXML spec for legal values
        return self.pairs["STATUS"]

class UxmlConnect:
    def __init__(self):#, HOST, PORT):
        self.m_login         = UxmlLoginMessage()
        self.m_login_resp    = UxmlLoginRespMessage()
        self.m_req           = UxmlPduMessage()
        self.m_resp          = UxmlPduMessage()
        self.m_connected     = False
        self.debug           = True
        self.MIN_COOKIE_LENGTH    = 6
        self.TIME_OUT_GRAIN   = 100
        self.MAX_TIME_OUT       = 20000 # 20 seconds
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
    def connect(self,r_sys,port,user,password,node_id,cookie,msec):
        self.m_login.setUser(user)
        self.m_login.setPassword(password)
        self.m_login.setRmt_Sys(r_sys) 
        self.m_login.setNode_Id(node_id)
        self.m_login.setCookie(cookie)
        self.m_push = "\xFF"
        host = r_sys[r_sys.find("@")+1:]
        if (self.debug): print("UxmlConnect: Connecting to: "+host)
       # self.disconnect();
        try:
            self.s.connect((host, port))
            self.s.settimeout(5)
            self.m_connected = True
            if (self.debug): print("UxmlConnect: Connected")
       # except socket.error:
        #    raise ConnectionError("Connection refused")
         #   self.m_connected = False
         #   return False
        except: 
            print "Unexpected error:", sys.exc_info()[0]
            raise
            self.m_connected = False
            return False
       
        msgOut = self.m_login.generateMessage()
        
        try:
            self.s.send("\x00")
            #self.s.recv(1024)
            #self.s.send(msgOut)
            totalsent = 0
            while totalsent < len(msgOut):
                try:
                    sent = self.s.send(msgOut[totalsent:])
                except:
                    print "Unexpected error:", sys.exc_info()[0]
                    raise
                if sent == 0:
                    raise RuntimeError, "transmit: connection broken!"
                totalsent = totalsent + sent
            if (self.debug): print("UxmlConnect: Send- "+msgOut)

            result = self.receiveMessage(self.m_login_resp)
            if (self.debug): print("UxmlConnect: Logging in...") 
        except:
            print "Unexpected error:", sys.exc_info()[0]
            raise
            print("Login send failed...")
            
        if (self.debug): print("UxmlConnect: Cookie:"+ self.m_login_resp.getCookie())     
        if (len(self.m_login_resp.getCookie()) < self.MIN_COOKIE_LENGTH):
            print("Invalid cookie")
            result = False;
        else:
            if (self.debug): print("UxmlConnect: Login succesful, cookie:"+self.m_login_resp.getCookie())
        self.m_connected = result
                        
        if (result == False):
            self.disconnect()   
             
        return result;
    
    def send(self,pdu):
        # Use the new cookie received from the remote system
        self.m_resp.setPairs(pdu.getPairs())
        self.m_resp.setCookie(self.m_login_resp.getCookie())
        
        # Make sure the output pipe is valid
        if self.isConnected():

            # Send the login    
            temp = self.m_resp.generateMessage()
            if (self.debug): print("UxmlConnect: Send- "+ temp);
            try:
                totalsent = 0
                while totalsent < len(temp):
                    try:
                        sent = self.s.send(temp[totalsent:])
                    except:
                        raise IOError, "Unable to send..."
                        
                    if sent == 0:
                        raise RuntimeError, "transmit: connection broken!"
    
                    totalsent = totalsent + sent
                if (self.debug): print("UxmlConnect: Send- "+temp)
            except:
                print("Socket Send Error")
                self.disconnect()
                return False
        else:
            print("UxmlConnect: Not connected")
            return False
        return True
    
    def receiveMessage(self,m_pdu):
        try: 
            response = self.s.recv(1024)
            print response
            while (not response):
                response += str(self.s.recv(1024))
        except socket.timeout:
            if (self.debug): print("UxmlConnect: Nothing received...")
            return False
        except:
            print "Unexpected error:", sys.exc_info()[0]
            raise
            self.disconnect()
        if ((response <> "") and (response <> self.m_push)):    
            m_pdu.parseMessage(response.replace(self.m_push,""))    
            return True    
        else: return False
        
    def receivePdu(self,pdu):
        try:
            result = self.receiveMessage(self.m_req)
            pdu.setPairs(self.m_req.getPairs());
            return result  
        except:
            return False
            print "UxmlConnect: Failed to interpret PDU"
            
    def isConnected(self):
        return self.m_connected
    
    def disconnect(self):
        #self.s.shutdown()
        self.m_connected = False
        self.s.close()
        return True
    
class UxmlMessage:
  
    # XML document prolog
    #m_prolog = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"
    
    def __init__(self):
    # Normalized data
        self.m_pairs = {}
    
    # Helper strings to make life easier
        self.m_root = "" #XML document name, eg login, ussd
        self.m_child = "" #XML document child, eg cookie
        self.m_name = "" #XML PDU name - used for debugging
        self.m_serialout = "" #DOM serialized output string
        self.m_push = "\xFF"

    def getParamCount(self):
        return len(self.m_pairs)
    
    def clearValues (self):
        for i,j in self.m_pairs.iteritems():
            self.m_pairs[i] = ""

    def getPairs(self):
        return self.m_pairs

    def setPairs(self,pairs):
        self.m_pairs = pairs

    def generateMessage(self):
        impl = minidom.DOMImplementation()
        doc = impl.createDocument(None, self.m_name, None)#None, "test", None)
        root = doc.createElement(self.m_root)
        # Create element
        for i,j in self.m_pairs.iteritems():
#            print i,j
            if i[:6]=="CHILD_":
                item = doc.createElement(self.m_child)
                #item.setAttribute(i[6:],j)
                root.appendChild(item)
            else:
                root.setAttribute(i,j)            
                
        # Add Root to Document
        doc.documentElement.appendChild(root)
        # Serialize the output
        m_serialout=doc.toxml(encoding="ISO-8859-1")
        m_serialout = m_serialout[:m_serialout.find("?>")+2]+"\x0A"+m_serialout[m_serialout.find("<"+self.m_root):m_serialout.find("</Uxml")]+self.m_push+self.m_push
        return m_serialout
    
    def parseMessage(self,message):
#        print("parsing...")
        try:
            xmldoc = minidom.parseString(message)
        except:
            print("Parse error!")
            print message
            return False
#        print xmldoc.toxml() 
        for i,j in self.m_pairs.iteritems():
            #print i
            #print self.m_root
            self.m_pairs[i] = xmldoc.getElementsByTagName(self.m_root)[0].getAttribute(i)
            #print self.m_pairs[i]
        return True
    
class UxmlPduMessage(UxmlMessage):
    def __init__(self):
        # Normalized data
            self.m_pairs={"PDU":"","MSISDN":"","STRING":"",\
                    "TID":"","REQID":"","ENCODING":"",\
                    "TARIFF":"","STATUS":"","CHILD_cookie":""}
        # Helper strings to make life easier
            self.m_root = "ussd" #XML document name, eg login, ussd
            self.m_child = "cookie" #XML document child, eg cookie
            self.m_name = "UxmlPduMessage" #XML PDU name - used for debugging
            self.m_push = "\xFF"

    def getParamCount(self):
        return len(self.m_pairs)
    

    def setPdu(self,pdu):
        self.m_pairs["PDU"] = pdu

    def getPdu(self):
        return self.m_pairs["PDU"]
    

    def setMsisdn(self,msisdn):
        self.m_pairs["MSISDN"]=msisdn
    
    def getMsisdn(self):
        return self.m_pairs["MSISDN"]

    def setString (self,string):
        self.m_pairs["STRING"] = string
    
    def getString(self):
        return vm_pairs["STRING"]

    def setTid(self,tid) :
        self.m_pairs["TID"] = tid
    

    def getTid(self):
        return self.m_pairs["TID"]
    
    def setReqid(self,reqid):
        self.m_pairs["REQID"] = reqid 

    def getReqid(self):
        return self.m_pairs["REQID"]
    
    def setEncoding (self,encoding) :
        self.m_pairs["ENCODING"] = encoding

    def getEncoding(self):
        return self.m_pairs["ENCODING"]

    def setTariff(self,tariff):
        self.m_pairs["TARIFF"] = tariff

    def getTariff(self):
        return self.m_pairs["TARIFF"]
    
    def setStatus(self,status):
        self.m_pairs["STATUS"] = status

    def getStatus(self):
        return self.m_pairs["STATUS"]
    
    def setCookie(self, cookie):
        self.m_pairs["CHILD_cookie"] = cookie
    
    def getCookie(self):
        return self.m_pairs["CHILD_cookie"]
       
class UxmlLoginMessage(UxmlMessage):

   def __init__(self):
       self.m_pairs = {"USER" : "","PASSWORD":"","RMT_SYS":"",\
                       "NODE_ID":"","COOKIE":""} 
       self.m_root = "login"
       self.m_name = "UxmlLoginMessage"
       self.m_push = "\xFF"
   
   def getParamCount(self) :
        return len(self.m_pairs)
    
    
   def setUser(self,user):
        self.m_pairs["USER"] = user
    
    
   def getUser(self):
        return self.m_pairs["USER"]
    
    
   def setPassword(self,password) :
        self.m_pairs["PASSWORD"] = password
    
    
   def getPassword(self):
        return self.m_pairs["PASSWORD"]
    
    
   def setRmt_Sys(self,rmt_sys):
        self.m_pairs["RMT_SYS"] = rmt_sys
    
    
   def getRmt_Sys(self):
        return self.m_pairs["RMT_SYS"]
    
    
   def setNode_Id(self,node_id):
        self.m_pairs["NODE_ID"] = node_id
    
    
   def getNode_Id(self):
        return self.m_pairs["NODE_ID"]
    
    
   def setCookie(self,cookie) :
        self.m_pairs["COOKIE"] = cookie
    
    
   def getCookie(self):
        return self.m_pairs["UCOOKIE"]

class UxmlLoginRespMessage(UxmlMessage):
    def __init__(self):
        self.m_pairs = {"VALUE":""}  
        self.m_root = "cookie";
        self.m_name = "UxmlLoginRespMessage";

    def getParamCount(self):
        return len(self.m_pairs)

    def setCookie(self):
        self.m_pairs["VALUE"] = cookie;
  
    def getCookie(self):
        return self.m_pairs["VALUE"]
    

